package cn.gs.math.fraction;

// @author gs
import java.math.BigInteger;

public class Fraction {

    private BigInteger up, down;

    public Fraction() {
        this(0);
    }

    public Fraction(long value) {
        this(value, 1);
    }

    public Fraction(long up, long down) {
        this(BigInteger.valueOf(up), BigInteger.valueOf(down));
    }

    public Fraction(BigInteger up, BigInteger down) {
        this.up = up;
        this.down = down;
        normalize();
    }

    private void normalize() {
        if (down.signum() < 0) {
            up = up.negate();
            down = down.negate();
        }
        BigInteger value;
        if (up.signum() >= 0) {
            value = up.gcd(down);
        } else {
            value = up.negate().gcd(down);
        }
        up = up.divide(value);
        down = down.divide(value);
    }

    public Fraction add(Fraction f) {
        BigInteger a = this.up.multiply(f.down).add(this.down.multiply(f.up));
        BigInteger b = this.down.multiply(f.down);
        return new Fraction(a, b);
    }

    public Fraction subtract(Fraction f) {
        BigInteger a = this.up.multiply(f.down).subtract(this.down.multiply(f.up));
        BigInteger b = this.down.multiply(f.down);
        return new Fraction(a, b);
    }

    public Fraction multiply(Fraction f) {
        BigInteger a = this.up.multiply(f.up);
        BigInteger b = this.down.multiply(f.down);
        return new Fraction(a, b);
    }

    public Fraction divide(Fraction f) {
        BigInteger a = this.up.multiply(f.down);
        BigInteger b = this.down.multiply(f.up);
        return new Fraction(a, b);
    }

    public Fraction pow(int exp) {
        BigInteger a = this.up.pow(exp);
        BigInteger b = this.down.pow(exp);
        return new Fraction(a, b);
    }

    public int signum() {
        return up.signum();
    }

    public boolean equal(Fraction f) {
        return up == f.up && down == f.down;
    }

    public boolean greaterThan(Fraction f) {
        return this.subtract(f).signum() > 0;
    }

    public boolean greaterThanOrEqual(Fraction f) {
        return this.subtract(f).signum() >= 0;
    }

    public boolean lessThan(Fraction f) {
        return this.subtract(f).signum() < 0;
    }

    public boolean lessThanOrEqual(Fraction f) {
        return this.subtract(f).signum() <= 0;
    }

    public Fraction max(Fraction f) {
        return this.greaterThan(f) ? this : f;
    }

    public Fraction min(Fraction f) {
        return this.greaterThan(f) ? f : this;
    }
}
